﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;

namespace Netnr.CodeBuilder.Core
{
    class BuilderTo
    {
        public class BuilderModel
        {
            public string tableName { get; set; }

            public string PrimaryKey { get; set; }

            public string DomainNamespace { get; set; }
            public string DomainClassName { get; set; }

            public string MappingNamespace { get; set; }
            public string MappingClassName { get; set; }

            public string RepositoryNamespace { get; set; }
            public string RepositoryClassName { get; set; }

            public string DomainFileName { get; set; }
            public string DomainFileExt { get; set; }
            public string DomainFileContent { get; set; }

            public string MappingFileName { get; set; }
            public string MappingFileExt { get; set; }
            public string MappingFileContent { get; set; }

            public string RepositoryFileName { get; set; }
            public string RepositoryFileExt { get; set; }
            public string RepositoryFileContent { get; set; }

            public bool DomainFieldComment { get; set; }
        }

        public static string BuilderContentDomain(BuilderModel bm)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("using System;");
            sb.Append(Environment.NewLine);
            sb.AppendFormat("namespace {0}", bm.DomainNamespace);
            sb.Append(Environment.NewLine);
            sb.AppendLine("{");
            sb.AppendFormat("\tpublic class {0}", bm.DomainClassName);
            sb.Append(Environment.NewLine);
            sb.AppendLine("\t{");

            sb.AppendLine("\t\tpublic " + bm.DomainClassName + "() { }");
            sb.AppendLine("\t\t#region Model");

            string sql = "select COLUMN_NAME,IS_NULLABLE,DATA_TYPE from information_schema.columns where table_name = '" + bm.tableName + "'";

            if (bm.DomainFieldComment)
            {
                sql += @";
                                SELECT  c.[name] ,CONVERT(VARCHAR(200), ep.value) AS[comment] 
                                FROM sys.tables AS t
                                     INNER JOIN sys.columns AS c ON t.object_id = c.object_id
                                        LEFT JOIN sys.extended_properties AS ep ON ep.major_id = c.object_id
                                                                                   AND ep.minor_id = c.column_id
                                WHERE ep.class = 1 AND t.name = '" + bm.tableName + "'";
            }

            DataSet ds = DbHelperSQL.GetDataSet(sql);

            foreach (DataRow dr in ds.Tables[0].Rows)
            {
                string field = dr["COLUMN_NAME"].ToString();
                string isnull = dr["IS_NULLABLE"].ToString() == "YES" ? "?" : "";
                string type = dr["DATA_TYPE"].ToString();

                string ct = DATA_TYPE_2[type];
                if (!"int,decimal,datetime,long,double,char".Contains(ct.ToLower()))
                {
                    isnull = "";
                }

                if (bm.DomainFieldComment)
                {
                    var dr_c = ds.Tables[1].Select("name='" + field + "'");
                    if (dr_c.Length > 0)
                    {
                        sb.AppendLine("\t\t/// <summary>");
                        sb.AppendLine("\t\t/// " + dr_c[0][1].ToString());
                        sb.AppendLine("\t\t/// <summary>");
                    }
                }

                sb.AppendLine("\t\tpublic " + ct + isnull + " " + field + " { get; set; }");

                sb.Append(Environment.NewLine);
            }

            sb.AppendLine("\t\t#endregion Model");
            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return sb.ToString();
        }

        public static string BuilderContentMapping(BuilderModel bm)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("using Microsoft.EntityFrameworkCore;");
            sb.AppendLine("using Netnr.Domain;");
            sb.AppendLine("using Microsoft.EntityFrameworkCore.Metadata.Builders;");
            sb.Append(Environment.NewLine);
            sb.AppendFormat("namespace {0}", bm.MappingNamespace);
            sb.Append(Environment.NewLine);
            sb.AppendLine("{");
            sb.AppendFormat("\tclass {0} : IEntityTypeConfiguration<{1}>", bm.MappingClassName, bm.DomainClassName);
            sb.Append(Environment.NewLine);
            sb.AppendLine("\t{");
            sb.AppendLine("\t\tpublic void Configure(EntityTypeBuilder<" + bm.DomainClassName + "> builder)");
            sb.AppendLine("\t\t{");
            string sql = "EXEC sp_pkeys '" + bm.tableName + "'";

            DataTable dt = DbHelperSQL.GetDataSet(sql).Tables[0];
            if (dt.Rows.Count > 0)
            {
                if (dt.Rows.Count == 1)
                {
                    bm.PrimaryKey = dt.Rows[0]["COLUMN_NAME"].ToString();
                }
                else
                {
                    bm.PrimaryKey = dt.Rows[0]["COLUMN_NAME"].ToString();
                }
            }
            sb.AppendLine("\t\t\tbuilder.HasKey(x => x." + bm.PrimaryKey + ");");
            sb.AppendLine("\t\t}");
            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return sb.ToString();
        }

        public static string BuilderContentRepository(BuilderModel bm)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("\t\tprivate RepositoryBase<" + bm.DomainClassName + "> _" + bm.tableName.ToLower() + "Repository;");
            sb.Append(Environment.NewLine);
            sb.AppendFormat("\t\tpublic RepositoryBase<{0}> {1}Repository", bm.DomainClassName, bm.tableName);
            sb.Append(Environment.NewLine);
            sb.AppendLine("{");
            sb.AppendLine("\t\t\tget");
            sb.AppendLine("\t\t\t{");
            sb.AppendFormat("\t\t\t\treturn _{0}Repository ?? (_{0}Repository = new RepositoryBase<{1}>(Context));", bm.tableName.ToLower(), bm.DomainClassName);
            sb.Append(Environment.NewLine);
            sb.AppendLine("\t\t\t}");
            sb.AppendLine("\t\t}");

            return sb.ToString();
        }

        public static bool BuilderUse(BuilderModel bm, string[] listUse)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(
@"using System;
using Netnr.Domain;

namespace Netnr.Data
{
    /// <summary>
    /// 统一入口，保证上下文（context）的一致性
    /// 如果不想手动释放资源，请用using
    /// </summary>
    public class RepositoryUse : IDisposable
    {
        #region Save & Dispose
        public int Save()
        {
            return Context.SaveChanges();
        }

        private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    Context.Dispose();
                }
            }
            disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region 上下文
        private ContextBase _context;

        /// <summary>
        /// 上下文
        /// </summary>
        public ContextBase Context
        {
            get
            {
                return _context ?? (_context = new ContextBase());
            }
        }
        #endregion

        #region 单表仓库

" + string.Join(Environment.NewLine, listUse) + @"
        #endregion
    }
}");

            string path = AppDomain.CurrentDomain.BaseDirectory + "BuilderResult\\Netnr.Data\\EFCore\\";
            string fileName = bm.RepositoryFileName + bm.RepositoryFileExt;

            return WriteFile(sb.ToString(), path, fileName);
        }

        public static bool BuilderDomain(BuilderModel bm)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "BuilderResult\\" + bm.DomainNamespace + "\\";
            string fileName = bm.DomainFileName + bm.DomainFileExt;

            return WriteFile(bm.DomainFileContent, path, fileName);
        }

        public static bool BuilderMapping(BuilderModel bm)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "BuilderResult\\" + bm.MappingNamespace + "\\";
            string fileName = bm.MappingFileName + bm.MappingFileExt;

            return WriteFile(bm.MappingFileContent, path, fileName);
        }

        public static bool WriteFile(string content, string path, string fileName)
        {
            bool b = false;
            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                string pne = path + fileName;

                if (File.Exists(pne))
                {
                    File.Delete(pne);
                }

                FileStream fs = new FileStream(pne, FileMode.Create);

                //流写入
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                sw.WriteLine(content);
                sw.Close();
                fs.Close();
                b = true;
            }
            catch (Exception)
            {

            }
            return b;
        }

        public static Dictionary<string, string> DATA_TYPE_1
        {
            get
            {
                Dictionary<string, string> dic = new Dictionary<string, string>();
                foreach (var type in Enum.GetValues(typeof(SqlDbType)))
                {
                    var item = Enum.GetName(typeof(SqlDbType), type);
                    dic.Add(item.ToString().ToLower(), item.ToString());
                }

                return dic;
            }
        }

        public static Dictionary<string, string> DATA_TYPE_2
        {
            get
            {
                Dictionary<string, string> dic = new Dictionary<string, string>();

                dic.Add("bigint", "long");
                dic.Add("binary", "object");
                dic.Add("bit", "bool");
                dic.Add("char", "string");
                dic.Add("datetime", "DateTime");
                dic.Add("decimal", "decimal");
                dic.Add("float", "double");
                dic.Add("image", "byte[]");
                dic.Add("int", "int");
                dic.Add("money", "decimal");
                dic.Add("nchar", "string");
                dic.Add("ntext", "string");
                dic.Add("numeric", "decimal");
                dic.Add("nvarchar", "string");
                dic.Add("real", "float");
                dic.Add("smalldatetime", "DateTime");
                dic.Add("smallint", "short");
                dic.Add("smallmoney", "decimal");
                dic.Add("text", "string");
                dic.Add("timestamp", "byte[]");
                dic.Add("tinyint", "byte");
                dic.Add("uniqueidentifier", "Guid");
                dic.Add("varbinary", "byte[]");
                dic.Add("varchar", "string");
                dic.Add("xml", "string");
                dic.Add("sql_variant", "object");

                return dic;
            }
        }
    }
}
